﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace server.app
{
    #region ObjectManager
    public sealed class ObjectManager
    {
        public int AllObjectCount { get { return _objDic.Count; } }
        public int FPS { get { return _timeStat.FPS; } }

        World _world = null;
        comm.UniqueID _uid = new comm.UniqueID("o");
        comm.TimeStat _timeStat = new comm.TimeStat();

        const int THREAD_SLEEP = 1000 / 40;

        Dictionary<string, Object> _objDic = new Dictionary<string, Object>();
        Dictionary<string, List<Object>> _playerObjDic = new Dictionary<string, List<Object>>();

        public ObjectManager(World world)
        {
            _world = world;

            comm.ThreadHelper.Run(() =>
            {
                while (true)
                {
                    _timeStat.Update();
                    lock (_objDic)
                    {
                        foreach (var obj in _objDic.Values)
                        {
                            obj.Update(_timeStat);
                        }
                    }
                    Thread.Sleep(THREAD_SLEEP);
                }
            });
        }

        void CheckCreate(Object obj)
        {
            var playerId = Player.ObjectIdToPlayerId(obj.ObjectId);
            if (playerId != null)
            {
                lock (_playerObjDic)
                {
                    if (!_playerObjDic.ContainsKey(playerId))
                    {
                        _playerObjDic.Add(playerId, new List<Object>());
                    }
                    _playerObjDic[playerId].Add(obj);
                }
            }
        }
        void CheckRemove(Object obj)
        {
            var playerId = Player.ObjectIdToPlayerId(obj.ObjectId);
            if (playerId != null)
            {
                lock (_playerObjDic)
                {
                    if (_playerObjDic.ContainsKey(playerId))
                    {
                        var list = _playerObjDic[playerId];
                        list.Remove(obj);
                        if (list.Count == 0)
                        {
                            _playerObjDic.Remove(playerId);
                        }
                    }
                }
            }
        }

        public Object[] GetObjectsByPlayerId(string playerId)
        {
            var arr = new Object[] { };
            lock (_playerObjDic)
            {
                if (_playerObjDic.ContainsKey(playerId))
                {
                    arr = _playerObjDic[playerId].ToArray();
                }
            }
            return arr;
        }
        public void RemoveObjectsByPlayerId(string playerId)
        {
            var objs = GetObjectsByPlayerId(playerId);
            var list = new List<string>();
            foreach (var obj in objs)
            {
                list.Add(obj.ObjectId);
            }
            RemoveObjects(list.ToArray());
        }
        public int ObjectsJoinSpace(string[] objectIds, string spaceId, int x, int y, Player player = null)
        {
            lock (_objDic)
            {
                var space = _world.SpaceManager.GetSpace(spaceId);
                if (space == null)
                {
                    return 0;
                }
                var list = new List<Object>();
                foreach (var objectId in objectIds)
                {
                    var obj = GetObject(objectId);
                    if (obj != null)
                    {
                        list.Add(obj);
                        if (obj.SObject != null)
                        {
                            obj.SObject.Space.RemoveSObjects(new Object[] { obj });
                        }
                    }
                }
                return space.CreateSObjects(list.ToArray(), x, y, player).Length;
            }
        }
        public Object CreateObject(string dObjectId, string objectId = null)
        {
            if (objectId == null)
            {
                objectId = _uid.NewId();
            }
            var obj = _world.DataManager.CreateObject(dObjectId, objectId);
            lock (_objDic)
            {
                if (!_objDic.ContainsKey(objectId))
                {
                    _objDic.Add(obj.ObjectId, obj);
                    CheckCreate(obj);
                    return obj;
                }
            }
            return null;
        }
        public int RemoveObjects(string[] objectIds, Player player = null)
        {
            lock (_objDic)
            {
                int count = 0;
                var dic = new Dictionary<Space, List<Object>>();
                foreach (var objectId in objectIds)
                {
                    var obj = GetObject(objectId);
                    if (obj != null)
                    {
                        if (obj.SObject != null)
                        {
                            if (!dic.ContainsKey(obj.SObject.Space))
                            {
                                dic.Add(obj.SObject.Space, new List<Object>());
                            }
                            dic[obj.SObject.Space].Add(obj);
                        }
                        _objDic.Remove(obj.ObjectId);
                        CheckRemove(obj);
                        count++;
                    }
                }
                foreach (var space in dic.Keys)
                {
                    space.RemoveSObjects(dic[space].ToArray(), player);
                }
                return count;
            }
        }
        public Object GetObject(string objectId)
        {
            Object obj;
            _objDic.TryGetValue(objectId, out obj);
            return obj;
        }
        public bool ObjectExists(string objectId)
        {
            return _objDic.ContainsKey(objectId);
        }
    }
    #endregion

    #region ObjAction
    public class ObjAction : comm.world.BAction
    {
        World _world = null;
        Object _obj = null;

        public ObjAction(World world, Object obj, Dictionary<string, comm.objst.ObjAction> acDic, string execAcKey)
            : base(acDic, execAcKey)
        {
            _world = world;
            _obj = obj;
        }

        protected override void OnExecBegin()
        {
            if (BeginExecKey != comm.objst.ObjActionType.IDLE.ToString() &&
                BeginExecKey != comm.objst.ObjActionType.MOVE.ToString())
            {
            }
            base.OnExecBegin();
        }
        protected override void OnExecEnd()
        {
            base.OnExecEnd();
        }
    }
    #endregion

    //#region Object
    public abstract class Object : comm.world.BObject
    {
        public comm.objst.ObjType ObjType { get; private set; }
        public string DObjectId { get; private set; }
        public string ObjectId { get; private set; }
        public SObject SObject { get; private set; }
        public Player Player
        {
            get
            {
                if (ObjType == comm.objst.ObjType.HERO || ObjType == comm.objst.ObjType.UNIT)
                {
                    return World.PlayerManager.GetPlayerByObjectId(ObjectId);
                }
                return null;
            }
        }
        public string PlayerId
        {
            get
            {
                var playerId = Player.ObjectIdToPlayerId(ObjectId);
                return playerId != null ? playerId : "";
            }
        }
        public ObjAction Action { get { return _objAction; } }
        public NValueManager NValueManager { get; private set; }
        public StateManager StateManager { get; private set; }

        public World World { get; private set; }

        object _nvalueLock = new object();
        ObjAction _objAction = null;
        comm.objst.DObject _dobj = null;
        comm.objst.DObject.Data _dobjData = null;

        public Object(World world, comm.objst.ObjType objType, string dObjectId, string objectId)
        {
            World = world;
            ObjType = objType;
            DObjectId = dObjectId;
            ObjectId = objectId;

            _dobj = World.DefineFile.GetObject(DObjectId);
            _dobjData = World.DefineFile.GetObjectData(DObjectId);

            _objAction = new ObjAction(world, this, _dobjData.ActionDic, comm.objst.ObjActionType.IDLE.ToString());

            NValueManager = new app.NValueManager(World, (comm.objst.NValueGroup)objType);
            NValueManager.OnNvalueChangeCallback = OnNValueChange;

            StateManager = new StateManager(World, this);
            StateManager.OnAddStateCallback = OnAddState;
            StateManager.OnRemoveStateCallback = OnRemoveState;
        }

        public void SetSObject(SObject sobj)
        {
            if (SObject == null)
            {
                SObject = sobj;
                SObject.MoveType = _dobj.MoveType;
                SObject.PathSize = (int)_dobj.PathSizeType;

                SObject.OnSetLocCallback = OnSetLoc;
                SObject.OnBeginMoveCallback = OnBeginMove;
                SObject.OnEndMoveCallback = OnEndMove;
                SObject.ReceiveResultOfObservedCallback = ReceiveResultOfObserved;

                OnEnterSpace(sobj.Space);
            }
        }
        public void ClearSObject()
        {
            if (SObject != null)
            {
                OnLeaveSpace(SObject.Space);
                SObject = null;
            }
        }
        public bool JoinSpace(string spaceId, int x, int y, Player player = null)
        {
            return World.ObjectManager.ObjectsJoinSpace(new string[] { ObjectId }, spaceId, x, y, player) == 1;
        }

        public comm.objst.SObjectInfo GetSObjectInfo()
        {
            var obj = new comm.objst.SObjectInfo
            {
                PlayerId = PlayerId,
                DObjectId = DObjectId,
                ObjType = ObjType,
                ObjectId = ObjectId
            };
            if (SObject != null)
            {
                obj.MoveSpeed = SObject.MoveSpeed;
                obj.MoveType = SObject.MoveType;
                obj.LocX = SObject.LocX;
                obj.LocY = SObject.LocY;
                obj.MoveTo = SObject.Moveing ? new comm.objst.MoveTo
                {
                    ObjectId = SObject.ObjectId,
                    FromX = SObject.FromX,
                    FromY = SObject.FromY,
                    ToX = SObject.ToX,
                    ToY = SObject.ToY,
                    TimeOffset = SObject.MoveTimeOffset
                } : null;
            }
            return obj;
        }

        protected virtual void OnSetLoc()
        {
        }
        protected virtual void OnBeginMove()
        {
        }
        protected virtual void OnEndMove()
        {
        }
        protected virtual void ReceiveResultOfObserved(comm.world.ObservedResultType type, comm.world.ObservedResult result)
        {
        }

        protected virtual void OnAddState(string stateId)
        {
        }
        protected virtual void OnRemoveState(string stateId)
        {
        }
        protected virtual void OnNValueChange(Dictionary<string, long> changes)
        {
        }

        protected virtual void OnEnterSpace(Space space)
        {
        }
        protected virtual void OnLeaveSpace(Space space)
        {
        }

        protected override void OnUpdate(comm.TimeStat timeStat)
        {
            _objAction.Update(timeStat);
            StateManager.Update(timeStat);
        }
    }
    //#endregion
}
